package com.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class Server implements Runnable{
	//1、多路复用器（管理所有通道)
	private Selector selector;
	//2、创建缓冲区
	private ByteBuffer readBuffer = ByteBuffer.allocate(1024);
	
	public Server(int port) {
		try {
			//打开多路复用器
			this.selector = Selector.open();
			//打开服务器通道
			ServerSocketChannel ssc = ServerSocketChannel.open();
			//设置服务器通道为非阻塞模式
			ssc.configureBlocking(false);
			//绑定地址
			ssc.bind(new InetSocketAddress(port));
			//把服务器通道注册到多路复用器上，并且监听阻塞事件
			ssc.register(this.selector, SelectionKey.OP_ACCEPT);
			
			System.out.println("服务器已经启动了,端口号:"+port);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void run() {
		while(true){
			try {
				//必须要使多路复用器开始监听
				this.selector.select();
				//返回多路复用器已经选择的结果集
				Iterator<SelectionKey> keys = this.selector.selectedKeys().iterator();
				//进行遍历
				while (keys.hasNext()) {
					//获取一个选择的元素
					SelectionKey key = keys.next();
					//取出来后就可以直接删除了
					keys.remove();
					if(key.isValid()){
						//如果为阻塞状态
						if(key.isAcceptable()){
							this.accept(key);
						}
						if(key.isReadable()){
							this.read(key);
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	/**
	 * 读取方法
	 * @param key
	 */
	private void read(SelectionKey key){
		try {
			//清空缓冲区的数据
			this.readBuffer.clear();
			//获取在通道上的socket通道对象
			SocketChannel sc = (SocketChannel) key.channel();
			//读取数据
			int count = sc.read(this.readBuffer);
			if(count == -1){
				key.channel().close();
				key.cancel();
				return;
			}
			//有数据则进行读取数据，读取数据之前需要进行复位操作(把position和limit进行复位)
			this.readBuffer.flip();
			//根据缓冲区数据长度创建相应大小的byte数组，接收缓冲区的数据
			byte []bytes = new byte[this.readBuffer.remaining()];
			//接收缓冲区数据
			this.readBuffer.get(bytes);
			//
			String body = new String(bytes).trim();
			System.out.println("服务端接收数据为：" + body);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	/**
	 * 阻塞方法
	 * @param key
	 */
	private void accept(SelectionKey key) {
		try {
			//获取服务器通道，
			ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
			//执行阻塞方法
			SocketChannel sc = ssc.accept();
			//设置阻塞模式
			sc.configureBlocking(false);
			//注册到多路复用器上，并设置读取标识
			sc.register(this.selector, SelectionKey.OP_READ);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		new Thread(new Server(8765)).start();
	}
}
